package com.webguard.outlierdetection.service.impl;

import com.webguard.outlierdetection.pojo.AnomalyRecord;
import com.webguard.outlierdetection.pojo.NetworkAnomaly;
import com.webguard.outlierdetection.repository.NetworkAnomalyRepository;
import com.webguard.outlierdetection.service.NetworkAnomalyService;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

@Service
public class NetworkAnomalyServiceImpl implements NetworkAnomalyService {
    private static final Logger logger = LoggerFactory.getLogger(NetworkAnomalyServiceImpl.class);
    private static final int BATCH_SIZE = 100;
    private static final int BATCH_INTERVAL = 5;

    private final BlockingQueue<NetworkAnomaly> batchQueue = new LinkedBlockingQueue<>(2000);
    private final NetworkAnomalyRepository repository;
    private final Executor saveExecutor;

    @Autowired
    public NetworkAnomalyServiceImpl(
            NetworkAnomalyRepository repository,
            @Qualifier("saveExecutor") Executor saveExecutor) {
        this.repository = repository;
        this.saveExecutor = saveExecutor;
        initBatchProcessor();
    }

    @PostConstruct
    public void initBatchProcessor() {
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
                this::flushBatch,
                BATCH_INTERVAL, BATCH_INTERVAL, TimeUnit.SECONDS
        );
    }

    @Override
    @Async
    public void saveAnomalyRecordAsync(AnomalyRecord record) {
        saveExecutor.execute(() -> {
            NetworkAnomaly anomaly = convertToEntity(record);
            if (!batchQueue.offer(anomaly)) {
                logger.warn("Batch queue full, saving directly");
                repository.save(anomaly);
            }
        });
    }

    private void flushBatch() {
        List<NetworkAnomaly> batch = new ArrayList<>(BATCH_SIZE);
        int drained = batchQueue.drainTo(batch, BATCH_SIZE);

        if (drained > 0) {
            logger.info("Attempting to save batch of {} records", drained);
            try {
                List<NetworkAnomaly> saved = repository.saveAll(batch);
                logger.info("Successfully saved {} records. Sample ID: {}",
                        saved.size(),
                        saved.isEmpty() ? "N/A" : saved.get(0).getId());
            } catch (Exception e) {
                logger.error("Batch save failed. Attempting individual saves", e);
                batch.forEach(item -> {
                    try {
                        NetworkAnomaly saved = repository.save(item);
                        logger.debug("Individually saved record ID: {}", saved.getId());
                    } catch (Exception ex) {
                        logger.error("CRITICAL: Failed to save record: {}", item, ex);
                    }
                });
            }
        }
    }

    private NetworkAnomaly convertToEntity(AnomalyRecord record) {
        return new NetworkAnomaly(
                record.getDestinationPort(),
                record.getFlowDuration(),
                record.getFlowBytesPerS(),
                record.getFlowPacketsPerS(),
                record.getSynFlagCount(),
                record.getRstFlagCount(),
                record.getAckFlagCount(),
                record.getType()
        );
    }
}
